/*
	D listener written by Christopher E. Miller
	This code is public domain.
	You may use it for any purpose.
	This code has no warranties and is provided 'as-is'.
*/

import std.concurrency, std.stdio, std.conv, std.socket;

import WsServer.ws;

void listnerproc()
{
	ushort port = 9999;

    // Cretae a socket and listen
	Socket listener = new TcpSocket;
	assert(listener.isAlive);
	listener.blocking = false;
	listener.bind(new InternetAddress(port));
	listener.listen(10);
	writeln("Listening on port: ", port);

    // Manage multiple connections
	const int MAX_CONNECTIONS = 60;
	SocketSet sset = new SocketSet(MAX_CONNECTIONS + 1); // Room for listener.
	Socket[] reads;
	WebSocket[] wsset;

    // At each iteration reset the socket set to empty
	for(;; sset.reset())
	{
	    // Add ourselves
		sset.add(listener);
		// Add all sockets from our socket collection
		foreach(Socket each; reads)
		{
			sset.add(each);
		}

        // Select on the socket set
        // Non-blocking, so we do not wait here
		Socket.select(sset, null, null);
		// After a select the socket set contains only those sockets that have changed state
        // Note that reads does not include the listner so we pick that up later
		int i;
		for(i = 0;; i++)
		{
			next:
			if(i == reads.length)
                // No more sockets in our collection
				break;

			if(sset.isSet(reads[i]))
			{
			    // This socket is in the socket set so is ready
				char[1024] buf;
				// Attempt to read
				int read = reads[i].receive(buf);
				if(Socket.ERROR == read)
				{
				    // Connection error
					writeln("Connection error.\n");
					goto sock_down;
				}
				else if(0 == read)
				{
				    // No data
					try
					{
						//if the connection closed due to an error, remoteAddress() could fail
						writeln("Connection from: ", reads[i].remoteAddress().toString(), " closed");
					}
					catch
					{
					}

					sock_down:
					reads[i].close(); //release socket resources now

					//remove from -reads-
					if(i != reads.length - 1)
						reads[i] = reads[reads.length - 1];
					reads = reads[0 .. reads.length - 1];

					writeln("\tTotal connections: ", reads.length);
                    // Very convoluted, but I thing the socket was removed from reads
					goto next; //-i- is still the next index
				}
				else
				{
				    // Here we have data
					//writeln("Received ", read, " bytes from ", reads[i].remoteAddress().toString(), " [data: ", buf[0 .. read], "]");

					// Send it to the correct instance of the web socket class
					wsset[i].feed(cast(string)buf[0 .. read]);
				}
			}
		}

		if(sset.isSet(listener)) //connection request
		{
		    // data on our listner socker means someone wants to connect
			Socket sn;
			try
			{
				if(reads.length < MAX_CONNECTIONS)
				{
				    // Allow the connection
					sn = listener.accept();
					writeln("Connection from ", sn.remoteAddress().toString(), " established" );
					assert(sn.isAlive);
					assert(listener.isAlive);

                    // Add the connected socket to our reads array
					reads ~= sn;
					writeln("Total connections: ", reads.length);

					// Create an instance of the web sockets class and stash it against this connection
					WebSocket ws = new WebSocket(reads[i]);
					wsset ~= ws;
				}
				else
				{
				    // Not allowed so reject
					sn = listener.accept();
					writeln("Rejected connection from ", sn.remoteAddress().toString(), ", too many connections");
					assert(sn.isAlive);

					sn.close();
					assert(!sn.isAlive);
					assert(listener.isAlive);
				}
			}
			catch(Exception e)
			{
				writeln("Error accepting: ", e.toString() );

				if(sn)
					sn.close();
			}
		}
	}
}


